/***************************************************************************
 *   Copyright (C) 2007 by Grzegorz Latarowski   *
 *   g.latarowski@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

// gcommon
#include "gexception.h"

// local
#include "marker.h"


const char * HOOK   = "hook";
const char * HOOK_X = "x";
const char * HOOK_Y = "y";


// ===========================================================================
//                                   GMarker
// ===========================================================================


// --------------------------- GMarker::GMarker() ----------------------------
GMarker::GMarker( QGraphicsItem * pItem )
	: GGraph( pItem )
{
	setFlags( ItemIsMovable );
	QGraphicsItem::setCursor( QCursor( Qt::SizeAllCursor ) );
	
		//set deaefult color as white
	_color = Qt::white;
	
	createHooks();
}
// ---------------------------------------------------------------------------


// --------------------------- GMarker::GMarker() ----------------------------
GMarker::GMarker( const QPointF hook1Pos, const QPointF hook2Pos,
				  QGraphicsItem * pItem )
	: GGraph( pItem )
{
	setFlags( QGraphicsItem::ItemIsMovable );
	QGraphicsItem::setCursor( QCursor( Qt::SizeAllCursor ) );
	
		//set default color as white
	_color = Qt::white;
	
	createHooks();
	
	setHooks( hook1Pos, hook2Pos );
}
// ---------------------------------------------------------------------------


// --------------------------- GMarker::GMarker() ----------------------------
GMarker::GMarker( const QColor color, QGraphicsItem * pItem )
	: GGraph( pItem ), _color( color )
{
	setFlags( QGraphicsItem::ItemIsMovable );
	QGraphicsItem::setCursor( QCursor( Qt::SizeAllCursor ) );
	
	createHooks();
}
// ---------------------------------------------------------------------------


// --------------------------- GMarker::GMarker() ----------------------------
GMarker::GMarker( const QPointF hook1Pos, const QPointF hook2Pos,
				  const QColor color, QGraphicsItem * pItem )
	: GGraph( pItem ), _color( color )
{
	setFlags( QGraphicsItem::ItemIsMovable );
	QGraphicsItem::setCursor( QCursor( Qt::SizeAllCursor ) );
	
	createHooks();
	
	setHooks( hook1Pos, hook2Pos );
}
// ---------------------------------------------------------------------------


// --------------------------- GMarker::~GMarker() ---------------------------
GMarker::~GMarker()
{
	delete _pHook1;
	delete _pHook2;
}
// ---------------------------------------------------------------------------


// --------------------------- GMarker::toXml() ------------------------------
QDomElement GMarker::toXml( QDomDocument & doc )const
{
	QDomElement markerElement = doc.createElement( this->name() );
	
		// write nodes 
	foreach( QPointF point, this->hooks() )
	{
			// create point
		QDomElement pointElement = doc.createElement( HOOK );
		markerElement.appendChild( pointElement );
		
			// create pos x element
		QDomElement xElement = doc.createElement( HOOK_X );
		pointElement.appendChild( xElement );
		QDomText xText = doc.createTextNode( QString::number( point.x() ) );
		xElement.appendChild( xText );
		
			// create pos y element
		QDomElement yElement = doc.createElement( HOOK_Y );
		pointElement.appendChild( yElement );
		QDomText yText = doc.createTextNode( QString::number( point.y() ) );
		yElement.appendChild( yText );
	}
	
		// succes 
	return markerElement;
}
// ---------------------------------------------------------------------------

	
// -------------------------- GMarker::fromXml() -----------------------------
void GMarker::fromXml( const QDomElement & element )
{
	QDomElement markerElement;
		// check if searched node isn't root 
	if( element.tagName() == this->name() )
	{
		markerElement = element;
	}
	else
	{
		markerElement = element.firstChildElement( this->name() );
	}
	
		// if there is no marker than throw an error 
	if( markerElement.isNull() )
	{
		// NOTE Maybe throw
		return;
// 		throw GDatasetError( tr( "Can not find '%1' node." ).arg( this->name() ) );
	}

		// create polygon for proper hooks 
	QPolygonF grabbedHooks;
	
	// ====================================================
	// parse first possible hook
	
		// search for hooks 
	QDomElement hookElement = markerElement.firstChildElement( HOOK );
		// if no hooks found than throw an error
	if( hookElement.isNull() )
	{
		// NOTE Maybe throw
		return;
// 		throw GDatasetError( tr( "Can not find '%1' node." ).arg( HOOK ) );
	}
		// parse X
	QDomElement xElement = hookElement.firstChildElement( HOOK_X );
		// parse Y
	QDomElement yElement = hookElement.firstChildElement( HOOK_Y );
		// if xElement and yElement were found then process its texts
	if( !xElement.isNull() && !yElement.isNull() )
	{
			// try to convert data
		bool okX, okY;
		double x = xElement.text().toDouble( &okX );
		double y = yElement.text().toDouble( &okY );
			// if ok than add hook to grabbedHooks 
		if( okX && okY )
		{
			grabbedHooks << QPointF( x, y );
		}
	}

	// ====================================================
	// parse next hooks
	while( !hookElement.nextSiblingElement().isNull() )
	{
		// ------------------------------------------------
		// search for next hook 
		hookElement = hookElement.nextSiblingElement();
			// parse X
		QDomElement xElement = hookElement.firstChildElement( HOOK_X );
			// if searching for x elemment failed then process next hook 
		if( xElement.isNull() )
		{
			continue;
		}
			// parse Y
		QDomElement yElement = hookElement.firstChildElement( HOOK_Y );
			// if searching for y elemment failed then process next hook
		if( yElement.isNull() )
		{
			continue;
		}
			// try to convert elements data
		bool okX, okY;
		double x = xElement.text().toDouble( &okX );
		double y = yElement.text().toDouble( &okY );
			// if ok than add hook to grabbedHooks 
		if( okX && okY )
		{
			grabbedHooks << QPointF( x, y );
		}
	}

		// check if at least 2 hook where found, if no throw an error 
	if( grabbedHooks.size() < 2 )
	{
		// NOTE Maybe throw
		return;
// 		throw GDatasetError( tr( "Not enough %1 nodes found required by %1." )
// 				.arg( HOOK ).arg( this->name() )
// 							  );
	}

		// everything seems to be ok, so grab first
		// two founded hooks and set marker
	setHooks( grabbedHooks[0], grabbedHooks[1] );
}
// ---------------------------------------------------------------------------


// ------------------------ GMarker::createHooks() ---------------------------
void GMarker::createHooks()
{
		// create hooks on top of marker
	_pHook1 = new GNode( this );
	_pHook2 = new GNode( this );
	_pHook1->setZValue( zValue() + 0.01 );
	_pHook2->setZValue( zValue() + 0.01 );
	
	_pHook1->setToolTip( tr( "Move hook around the scene to rotate the marker." ) );
	_pHook2->setToolTip( tr( "Move hook around the scene to rotate the marker." ) );

		// setup hooks shape 
// 	QPainterPath hookShape;
// 	hookShape.moveTo( -5,   5 );
// 	hookShape.lineTo(  0, -10 );
// 	hookShape.lineTo(  5,   5 );
// 	hookShape.closeSubpath();

// 	_pHook1->setShape( hookShape );
// 	_pHook2->setShape( hookShape );
}
// ---------------------------------------------------------------------------


// --------------------------- GMarker::setZValue() --------------------------
void GMarker::setZValue( const qreal z )
{
		// change items z value
	QGraphicsItem::setZValue( z );
		// change hooks z value, hook on top of marker
	if ( _pHook1 ) _pHook1->setZValue( zValue() + 0.01 );
	if ( _pHook2 ) _pHook2->setZValue( zValue() + 0.01 );
}
// ---------------------------------------------------------------------------


// --------------------- GMarker::setHooks() ---------------------------------
void GMarker::setHooks( const QPointF & pos1, const QPointF & pos2 )
{
	if( scene() ) _boundingRect = scene()->sceneRect();
	
	if( _pHook1 ) _pHook1->setPos( pos1 );
	if( _pHook2 ) _pHook2->setPos( pos2 );

	adjust();
}
// ---------------------------------------------------------------------------


// --------------------- GMarker::hooks() ------------------------------------
QPolygonF GMarker::hooks()const
{
	QPolygonF hooks;
	
	if( _pHook1 ) hooks << _pHook1->pos();
	if( _pHook2 ) hooks << _pHook2->pos();
	
	return hooks;
}
// ---------------------------------------------------------------------------


// ----------------------- GMarker::boundingRect() ---------------------------
QRectF GMarker::boundingRect() const
{
	return _boundingRect;
}
// ---------------------------------------------------------------------------


// -------------------------- GMarker::shape() -------------------------------
QPainterPath GMarker::shape() const
{
	double extra = 4;
	
	// create strip of the size 2*extra
	
	QLineF forward( _markerLine.p1(), _markerLine.p2() );
	QLineF fNormal = forward.normalVector();
	fNormal.setLength( extra );
	QPointF p1 = fNormal.p2();
	fNormal.setLength( -2*extra );
	QPointF p2 = fNormal.p2();
	
	QLineF backward( _markerLine.p2(), _markerLine.p1() );
	QLineF bNormal = backward.normalVector();
	bNormal.setLength( extra );
	QPointF p3 = bNormal.p2();
	bNormal.setLength( -2*extra );
	QPointF p4 = bNormal.p2();
	
	QPainterPath shape( p1 );
	shape.lineTo( p2 );
	shape.lineTo( p3 );
	shape.lineTo( p4 );
	shape.closeSubpath();
	return shape;
}
// ---------------------------------------------------------------------------


// ------------------------- GMarker::paint() --------------------------------
void GMarker::paint( QPainter * pPainter,
					 const QStyleOptionGraphicsItem * option,
					 QWidget * widget )
{
	Q_UNUSED( widget );
	if ( !pPainter ) return;
	
	QPen pen( _color );
	if (option->state & QStyle::State_Sunken )
        pen.setColor( _color.light() );
	
	pPainter->setPen( pen );
	pPainter->drawLine( _markerLine );
	
		// draw colored path
	pPainter->setPen( QPen( Qt::NoPen ) );
	QColor color( _color );
	color.setAlpha( 80 );
	pPainter->setBrush( color );
	pPainter->drawPath( _coloredPath );
}
// ---------------------------------------------------------------------------


// ---------------------- GMarker::adjust() ----------------------------------
void GMarker::adjust()
{
		// if hooks not exists return empty line
	if( !_pHook1 || !_pHook2 )
	{
		_markerLine = QLineF();
		_coloredPath = QPainterPath();
		return;
	}
	
	if( scene() ) _boundingRect = scene()->sceneRect();
	
	// calculate line through hooks starting
	// and ending on the edge of _boundingRect
	
	// create lines which will help searching intersecting points
	double expanding = _boundingRect.width() + _boundingRect.height();

		// start line expands through startPoint
	QLineF startLine( mapFromScene( _pHook2->pos() ),
					  mapFromScene( _pHook1->pos() )
					);
	startLine.setLength(
			qMax( _boundingRect.width(), _boundingRect.height() ) + expanding
					   );
		// end line expands through endPoint
	QLineF endLine( mapFromScene( _pHook1->pos() ),
					mapFromScene( _pHook2->pos() )
				  );
	endLine.setLength(
			qMax( _boundingRect.width(), _boundingRect.height() ) + expanding
					 );
	
		// _boundingRect edges
	QLineF left( mapFromScene( _boundingRect.bottomLeft() ),
				 mapFromScene( _boundingRect.topLeft() ) );
	QLineF top( mapFromScene( _boundingRect.topLeft() ),
				mapFromScene( _boundingRect.topRight() ) );
	QLineF right( mapFromScene( _boundingRect.topRight() ),
				  mapFromScene( _boundingRect.bottomRight() ) );
	QLineF bottom( mapFromScene( _boundingRect.bottomRight() ),
				   mapFromScene( _boundingRect.bottomLeft() ) );
	
		// create vector from boundingRect edges
	QVector< QLineF > edges;
	edges << left << top << right << bottom;
	
		// search for startPoint and endPoint through edges
		// store edges numbers
	QPointF startPoint, endPoint;
	int startEdgeNum = -1;
	int endEdgeNum = -1;
	for( int i = 0; i < edges.size(); i++ )
	{
		QPointF intersectingPoint;
			// if edge intersects with startPoint expaned line than
			// this is markers starting point
		if( startLine.intersect( edges[i], & intersectingPoint )
				  == QLineF::BoundedIntersection )
		{
			startPoint = intersectingPoint;
			startEdgeNum = i;
		}
			// this must be markers endPoint
		if( endLine.intersect( edges[i], & intersectingPoint )
				  == QLineF::BoundedIntersection )
		{
			endPoint = intersectingPoint;
			endEdgeNum = i;
		}
	}
	
		// create colored path
	QPainterPath coloredPath;
	if( startEdgeNum <= endEdgeNum )
	{
		coloredPath.moveTo( startPoint );
		for( int i = startEdgeNum; i < endEdgeNum; i ++ )
		{
			coloredPath.lineTo( edges[i].p2() );
		}
		
		coloredPath.lineTo( endPoint );
		coloredPath.closeSubpath();
	}
	else
	{
		coloredPath.moveTo( startPoint );
		for( int i = startEdgeNum; i < edges.size(); i++ )
		{
			coloredPath.lineTo( edges[i].p2() );
		}
		
        for( int i = 0; i < endEdgeNum; i++ )
		{
			coloredPath.lineTo( edges[i].p2() );
		}
		
		coloredPath.lineTo( endPoint );
		coloredPath.closeSubpath();
	}

	rotateNodes();
	
		// update all stuff
	removeFromIndex();
	_markerLine = QLineF( startPoint, endPoint );
	_coloredPath = coloredPath;
	addToIndex();

		// say item changed if not forced setting
	emit itemChanged();
	
	update();
}
// ---------------------------------------------------------------------------


// -------------------- GMarker::rotateNodes() -------------------------------
void GMarker::rotateNodes()
{
	// TODO finish hooks rotating
	return;
	
	
	if( !_pHook1 || !_pHook2 )
	{
		return;
	}

	// calculate rotation angle
	
	static double currentAngle; //< stores current rotation angle
	
	QLineF markerLine( mapFromScene( _pHook1->pos() ),
					   mapFromScene( _pHook2->pos() )
					 );
	QLineF vLine( 0, 0, 1, 0 );
	QLineF hLine( 0, 0, 0, 1 );

	double newAngleV = vLine.angle( markerLine );
	double newAngleH = hLine.angle( markerLine );

	double newAngle = newAngleV;
	if( newAngleH > 90 )
	{
		newAngle = 180 + ( 180 - newAngleV );
	}

	_pHook1->rotate( newAngle - currentAngle );
	_pHook2->rotate( newAngle - currentAngle );

	currentAngle = newAngle;
}
// ---------------------------------------------------------------------------


// -------------------------- GMarker::mouseMoveEvent() ----------------------
void GMarker::mouseMoveEvent( QGraphicsSceneMouseEvent * event )
{
	QPointF step = event->scenePos() - event->lastScenePos();
	
	if( _pHook1 )
	{
		_pHook1->moveBy( step.x(), step.y() );
	}
	if( _pHook2 )
	{
		_pHook2->moveBy( step.x(), step.y() );
	}
	
		// update marker
	adjust();
}
// ---------------------------------------------------------------------------


// eof
